/**
 * TerminateNodes.java
 * ************************** 
 * @date Jun 1, 2011
 * @author Michele Tamburini
 * 
 * **************************
 */
package infrastructureAPI.algorithms.instanceTools;

import gossipServices.basic.nodeDescriptors.GeneralNodeDescriptor;
import gossipServices.basic.nodeDescriptors.NodeDescriptor;
import gossipServices.basic.view.PartialView;
import gossipServices.bootstraping.tman.TManService;
import infrastructureAPI.Requester;
import infrastructureAPI.Service;
import infrastructureAPI.APIMessages.RequestMessage;
import infrastructureAPI.algorithms.GeneralAPIFunctionAlgorithm;
import infrastructureAPI.algorithms.instanceTools.facilities.TManStopper;
import infrastructureAPI.algorithms.instanceTools.facilities.TManViewNodeRemover;
import infrastructureAPI.backendGate.APIScript;
import infrastructureAPI.backendGate.SharedAPIParameters;

import java.net.InetAddress;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.List;
import java.util.Vector;

import networkRmi.NetworkManager;

/**
 * Description: 
 * Terminates the inputed nodes. 
 *
 */
public class TerminateNodes extends GeneralAPIFunctionAlgorithm
	implements APIScript{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	protected static int mandatoryParameters = 1;
	protected List<NodeDescriptor> targetNodes;
	protected String subcloudID = null;

	/**
	 * 
	 */
	public TerminateNodes() { }

	/**
	 * @param requester
	 */
	public TerminateNodes(Requester requester, List<NodeDescriptor> targetNodes) {
		super(requester);
		this.targetNodes = targetNodes;
	}
	
	public TerminateNodes(Requester requester, List<NodeDescriptor> targetNodes,
			String subcloudID) {
		super(requester);
		this.targetNodes = targetNodes;
		this.subcloudID = subcloudID;
	}

	@Override
	public RequestMessage createRequestMessage(InetAddress targetAddress,
			String targetNodeName) {
		return defaultRequestMessageComposition(targetAddress, targetNodeName);
	}


	/**
	 * @param netMan
	 * @param overlay
	 * @return
	 */
	private boolean removeTargetNodesFromOverlay(NetworkManager netMan,
			PartialView overlay) {
		TManStopper stopper = new TManStopper(requester);
		RequestMessage requestMsg ;
		
		// into targetNodes we have only the nodeName. So we have to retrieve
		// the address by looking into the overlay.
//		System.out.println(this.getClass().getSimpleName()+" targetNodes:\n"+targetNodes);
		for(NodeDescriptor node : targetNodes){
			try {
				NodeDescriptor realNodeDescriptor = overlay.find(node.getNodeName());
				if(realNodeDescriptor != null){
					requestMsg =  stopper.createRequestMessage(
							realNodeDescriptor.getNodeAddress(),
							realNodeDescriptor.getNodeName());
					System.out.println("Try to terminate node: "+realNodeDescriptor);
					//						+"\nwith message: "+requestMsg);
					netMan.send(requestMsg, realNodeDescriptor.getNodeAddress());

					// we have also to remove the current node from the overlay because
					// later we are going to use the overlay variable to send message
					// to the remaining nodes.
					overlay.remove(realNodeDescriptor);
				}
			} catch (RemoteException e) {
				e.printStackTrace();
			} catch (NotBoundException e) {
				e.printStackTrace();
			}
		}
		return true;
	}

	/**
	 * @return
	 */
	private boolean updateViewOfOtherNodes(NetworkManager netMan,
			PartialView overlay) {
		TManViewNodeRemover viewRemover = new TManViewNodeRemover(requester, targetNodes);
		RequestMessage requestMsg2 ; 
		for(int i=0; i < overlay.size(); i++){
			NodeDescriptor node = overlay.getAt(i);
			
			/* the local node isn't in the overlay PartialView, so
			 * it will not receive the TManViewNodeRemover message.
			 */
			if(!targetNodes.contains(node)){
				
				// create a request message with 'node' as the target node
				requestMsg2 = viewRemover.createRequestMessage(node.getNodeAddress(), node.getNodeName());
				System.out.println("Update view of: "+node);
//						+"\nwith message: "+requestMsg2);
				try {
					netMan.send(requestMsg2, node.getNodeAddress());
				} catch (RemoteException e) {
					e.printStackTrace();
				} catch (NotBoundException e) {
					e.printStackTrace();
				}
			}
		}
		return true;
	}

	@Override
	public Service extractServiceToDeRegister() {
		return null;
	}

	@Override
	public Service extractSuppliedService() {
		return null;
	}

	@Override
	public boolean requireSystemDeRegistration() {
		return false;
	}

	@Override
	public boolean requireSystemRegistration() {
		return false;
	}

	@Override
	public List<Class<?>> requiredServices() {
		List<Class<?>> list = new Vector<Class<?>>();
		list.add(NetworkManager.class);
		list.add(TManService.class);
		return list;
	}

	@Override
	public int mandatoryParametersNumber() {
		return mandatoryParameters;
	}

	@Override
	public String showUsage() {
		String ret = new String("");
		ret += "terminate-nodes "+ SharedAPIParameters.SUBCLOUD_ID_OPTION +
			"<subcloud> [[node1] [node2]... [nodeN]]\n";		
		return ret;
	}

	@Override
	public boolean requireReply() {
		return true;
	}

	@Override
	public void run() {
		boolean ret = true;
		
		// Service(s) retrieval:
		// ------------------------------------
		NetworkManager netMan = (NetworkManager) services.get(0);
		TManService tman = (TManService) services.get(1);
		NodeDescriptor localNode = new GeneralNodeDescriptor(netMan.getNodeName());
		
		// Algorithm:
		// ------------------------------------
		PartialView overlay = tman.getOverlayNeighbors();
		
		/* We can divide the termination in two steps:
		 	1st) stop the TMan service on the targetNodes
		 	2nd) remove the targetNodes from the ParialView of other
		 		 remaining nodes in the overlay.
		*/
				
		// 1st STEP
		// has the local node to stop TMan ?
		if(targetNodes.contains(localNode)){
			tman.stopTMan();
		}
		ret = removeTargetNodesFromOverlay(netMan, overlay);
		
		// 2nd STEP
		ret = updateViewOfOtherNodes(netMan, overlay);
				
		returnValue = ret;
	}

}
